#include "command.h"


//私有通信协议， 因为TCP通信是字节流，没有边界，
//通过私有协议，能让发送接收方遵守确定的边界，接收数据
//定义一个小火车的私有协议
//typedef struct train_t {
//    int length;
//    char buf[BUFFERSIZE];
//} train_t;

//获取文件长度信息
off_t statSize(int fileFd)
{
    struct stat statbuf;
    memset(&statbuf, 0, sizeof(statbuf));
    int ret = fstat(fileFd, &statbuf);
    if (-1 == ret) {
        perror("fstat");
        return -1;
    }
    return statbuf.st_size;
}

//选择使用哪一种方式接收文件
int choiceFile(int peerfd, const char* filename)
{
    int tag = isInserver(filename);
    if (0 == tag) {
        send(peerfd, &tag, sizeof(tag), 0);
        printf("断点续传。\n");
        recv_interrupt_file(filename, peerfd);
        return 1;
    }
    else {
        send(peerfd, &tag, sizeof(tag), 0);
        printf("新文件传输。\n");
        recv_file(peerfd);
        return 0;
    }
}

//接收客户端文件--断点续传, const定义文件名
int recv_interrupt_file(const char* filename, int peerfd)
{//两个文件描述符，peerfd用于通信， fd为打开的文件的文件描述符
    //peerfd通信也是文件对象， Linux一切皆文件
    train_t1 train;
    memset(&train, 0, sizeof(train));
    int len;


    int fd = open(filename, O_RDWR);
    ERROR_CHECK_G(fd, -1, "open");

    //当前文件大小
    off_t fileCurSize = statSize(fd);

    //将文件偏移量设置到末尾
    off_t filenow = lseek(fd, 0, SEEK_END);
    //发送当前文件位置--未用小火车
    int ret = send(peerfd, &filenow, sizeof(off_t), 0);
    ERROR_CHECK_G(ret, -1, "send");

    //接收剩余文件大小--阻塞
    off_t fileRemain = 0;
    ret = recv(peerfd, &fileRemain, sizeof(off_t), MSG_WAITALL);
    printf("fileRemain = %ld\n", fileRemain);

    //定义在while之外
    off_t fileSumSize = fileCurSize + fileRemain;
    off_t slice = fileSumSize / 100;
    int percent = fileCurSize / slice;  //当前接收了多少--到这里是对的


    off_t downloadSize = 0;
    off_t lastSize = 0;
    char recvBuff[BUFFERSIZE];
    //接收文件--小火车协议接收
    while (downloadSize < fileRemain) {
        ret = recv(peerfd, &len, 4, MSG_WAITALL);
        ERROR_CHECK_G(ret, -1, "recv in interrupt recv");
        if (0 == ret) {  //最后一次没有信息，发送过来的长度是零---没进这个循环
            puts("file has recv all data");
            break;
        }
        memset(recvBuff, 0, BUFFERSIZE);

        ret = recv(peerfd, recvBuff, len, MSG_WAITALL);
        ERROR_CHECK_G(ret, -1, "interrupt recv");
        downloadSize += ret;

        //打印下载百分比的代码，用整数实现
        if (downloadSize - lastSize >= slice ) {
            percent++;
            printf(">> download percent: %d\n", percent);
            lastSize += slice;  //这样是正好的百分比
        }

        ret = write(fd, recvBuff, ret);
        ERROR_CHECK_G(ret, -1, "write in recv_interrupt_file");
    }
    printf(">> download percent: %d\n", 100);
    return 0;
}


//接收客户端上传的文件
int recv_file(int fd)
{
    int ret = -1;
    //1、接收文件名, 发送过来的命令，已经知道文件名了，还有必要接收文件名吗
    int len;
    ret = recv(fd, &len, 4, 0);  //int len来接收的文件名长度
    ERROR_CHECK_G(ret, -1, "recv");

    char recv_buf[BUFFERSIZE] = {0};
    ret = recv(fd, recv_buf, len, 0);
    printf(">>2 recv filename: %s\n", recv_buf);

    //2、在服务器创建文件
    int fd_file = open(recv_buf, O_WRONLY | O_TRUNC | O_CREAT, 0666);
    ERROR_CHECK_G(ret, -1, "recv");

    //更改服务器端文件默认的root权限
   // mode_t mode;
   // sscanf("664", "%o", &mode);
   // ret = fchmod(fd_file, mode);
   // ERROR_CHECK(ret, -1, "fchmod");
   // 改不了是root用户名下，没必要非得改root用户


    //TCP是一个字节流协议！----注意
    //3、接收文件大小，，接收到的是字符串格式数据
    off_t file_size;
    ret = recv(fd, &len, 4, MSG_WAITALL);
    ERROR_CHECK_G(ret, -1, "recv");
    ret = recv(fd, &file_size, sizeof(file_size), MSG_WAITALL);
    ERROR_CHECK_G(ret, -1, "recv");
    printf(">> file length1: %ld\n", file_size);

    //mmap使用---必须文件大小确定
    //mmap pbuf: Permission denied.
    //if (file_size > 1<<20) {  //100<<20
    //     //mmap映射， 确定的文件大小
    //     puts("mmap ok");

    //     ftruncate(fd_file, file_size);

    //     char* pbuf = (char* )mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_file, 0);
    //     ERROR_CHECK(pbuf, MAP_FAILED, "mmap pbuf");

    //     ret = recvn(fd, pbuf, file_size);
    //     if (-1 == ret) {//  mmap不需要write;
    //         puts ("recvn in mmap failed");
    //         return -1;
    //     }

    // }
    // else {
    off_t slice = file_size / 100;
    //4、接收文件内容
    off_t download_size = 0;
    off_t last_size = 0;
    int percent = 0;  //定义了while外
    while (download_size < file_size) {
        ret = recv(fd, &len, 4, MSG_WAITALL);  //小火车文件长度
        ERROR_CHECK_G(ret, -1, "recv");
        if (0 == ret) {  //最后一次没有信息，发送过来的长度是零
            puts("file has recv all data");
            break;
        }

        memset(recv_buf, 0, BUFFERSIZE);
        ret = recv(fd, recv_buf, len, MSG_WAITALL);  //小火车文件内容
        ERROR_CHECK_G(ret, -1, "recv");

        download_size += ret;

        //打印下载百分比的代码，用整数实现
        if (download_size - last_size > slice ) {
            percent++;
            printf(">> download percent: %d\n", percent);
            last_size += slice;  //这样是正好的百分比
        }

        //5、写入本地
        ret = write(fd_file, recv_buf, ret);
        ERROR_CHECK_G(ret, -1, "write");

    }
    //}

    //4、关闭打开文件的文件描述符
    close(fd_file);

    return 0;
}

//实现gets 命令
int transferFile(int peerfd,const char *filename){
    train_t1 train;
    int ret;
    memset(&train,0,sizeof(train));
    //1传输文件名给对端

    //2读取本地文件
    int fileFd=open(filename,O_RDWR);

    //3.发送文件的长度
    struct stat st;
    fstat(fileFd,&st);          //获取文件长度
    memset(&train,0,sizeof(train));
    train.len=sizeof(off_t);     //4
    printf("file length=%ld.\n",st.st_size);
    strncpy(train.buff,(char*)&st.st_size,sizeof(off_t));
    ret=sendn(peerfd,&train,4+train.len);

    //4发送文件
    if (st.st_size > 1<< 20) {
        //采用mmap方式，需文件大小是确定的
        puts("mmap used");
        //省去了read
        char* pbuf = (char* )mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fileFd, 0);
        ERROR_CHECK(pbuf, MAP_FAILED, "mmap pbuf");

        ret = sendn(peerfd, pbuf, st.st_size);
        if (-1 == ret) {
            puts("sendn in mmap failed");
            return -1;
        }
    }
    else {
        off_t total=0;
        char sendBuff[BUFFERSIZE];
        while(total<st.st_size){
            memset(sendBuff,0,sizeof(sendBuff));
            ret=read(fileFd,train.buff,BUFFERSIZE);
            ERROR_CHECK(ret,-1,"read");
            train.len=ret;
            ret=sendn(peerfd,&train,4+train.len);
            total+=train.len;
        }
    }

    printf("download!\n");
    close(fileFd);
    return 0;
}

int choiceHow(int peerfd,const char *filename){
    int tag;
    recv(peerfd,&tag,sizeof(int),MSG_WAITALL);
    if(tag==0){
        printf("断点续传。\n");
        sendFile(peerfd,filename);
        return 0;
    }else{
        printf("新文件传输。\n");
        transferFile(peerfd,filename);
        return -1;
    }
}

int isInserver(const char *filename){
    DIR *pdir=opendir(".");
    ERROR_CHECK(pdir,NULL,"opendir");

    struct dirent* pdirent;
    while((pdirent=readdir(pdir))!=NULL){           //遍历当前目录，查询是否有filename的文件
        char *name=pdirent->d_name;
        if(strcmp(name,filename)==0){
            closedir(pdir);
            return 0;
        }
    }
    closedir(pdir);
    return -1;
}

int sendFile(int peerfd,const char* filename){
    off_t file_lseek;
    off_t last_lseek;
    off_t left_lseek;
    train_t1 train;
    bzero(&train,sizeof(train));

    int ret=recv(peerfd,&file_lseek,sizeof(off_t),MSG_WAITALL);
    ERROR_CHECK(ret,-1,"recv");

    int fileFd=open(filename,O_RDWR);

    last_lseek=lseek(fileFd,-1,SEEK_END);
    left_lseek=last_lseek-file_lseek;
    ret=send(peerfd,&left_lseek,sizeof(off_t),0);

    lseek(fileFd,file_lseek,SEEK_SET);      //将文件偏移量设置到响应位置

    off_t total=0;
    while(total<left_lseek){
        memset(&train,0,sizeof(train));
        ret=read(fileFd,train.buff,BUFFERSIZE);
        ERROR_CHECK(ret, -1, "send");

        train.len=ret;
        ret=sendn(peerfd,&train,4+train.len);
        total+=train.len;
    }
    close(fileFd);
    return 0;
}

//成功返回发送的字节数， 错误返回-1；
int sendn(int fd, const void* str, int len)
{
    char*  train= (char* )str;
    int left = len;
    int ret = -1;

    while (left > 0) {
        ret = send(fd, train, left, 0);
        if (-1 == ret) {
            perror("send");
            return -1;
        }
        else {
            train += ret;
            left -= ret;
        }
    }
    return len - left;
}
//成功返回接收的字节数， 错误返回-1；
int recvn(int fd, const void* str, int len)
{
    char* buf = (char* )str;
    int left = len;
    int ret = -1;

    while (left > 0) {
        ret = recv(fd, buf, left, 0);
        if (-1 == ret) {
            perror("recv");
            return -1;
        }
        buf += ret;
        left -= ret;
    }
    return len -left;
}
